package com.battch.netty;

import com.battch.netty.battcn_netty_7_1_1_protobuf.proto.OrderProto;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

public class OrderServer {

	public static void bind(int port) {
		EventLoopGroup masterGroup = new NioEventLoopGroup();//线程组,含一组NIO线程,专门用来处理网络事件
	    EventLoopGroup workerGroup = new NioEventLoopGroup();
	   
	    ServerBootstrap bootstrap = new ServerBootstrap();//NIO服务端启动辅助类
	    bootstrap.group(masterGroup, workerGroup).channel(NioServerSocketChannel.class)
        .option(ChannelOption.SO_BACKLOG, 100)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel channel) throws Exception {
            	
            	//重写initChannel方法，相比ObjectEncoder，
    			//使用Protobuf相对复杂一点，需要配置ProtobufVarint32FrameDecoder半包处理，
    			//随后添加ProtobufDecoder解码器，告知Protobuf需要对那个POJO进行解码操作，
                //否则从字节中我们是无法知道目标类型…
            	
            	
				//1. ProtobufVarint32LengthFieldPrepender()
				//对protobuf协议的的消息头上加上一个长度为32的整形字段，用于标志这个消息的长度。
				//这里是官方举的例子，实际这个字段的长度是5byte
            	
            	//2. ProtobufVarint32FrameDecoder()
            	//这是针对protobuf协议的ProtobufVarint32LengthFieldPrepender()所加的长度属性的解码器
            	
                channel.pipeline().addLast(new ProtobufVarint32FrameDecoder()); //解码 
                channel.pipeline().addLast(new ProtobufDecoder(OrderProto.OrderRequest.getDefaultInstance()));
                channel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender()); //封装格式 
                channel.pipeline().addLast(new ProtobufEncoder());  
                channel.pipeline().addLast(new OrderServerHandler());
                
                
                
                
            }
        });
	    
	    //绑定端口，同步等待成功,
        System.out.println("绑定端口,同步等待成功......");
        try {
			ChannelFuture future = bootstrap.bind(port).sync();
			//等待服务端监听端口关闭
			future.channel().closeFuture().sync();
			System.out.println("等待服务端监听端口关闭......");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
            //优雅退出释放线程池
            masterGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            System.out.println("优雅退出释放线程池......");
        }
		
	}
	
	
	private static class OrderServerHandler extends ChannelHandlerAdapter {
		
		@Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            OrderProto.OrderRequest request = (OrderProto.OrderRequest) msg;
            System.out.println("Service Accept Client Order Request :[" + request + "]");
            System.out.println(request.getOrderId() + "===================");
            
            
		
            
            OrderProto.OrderResponse.Builder builder = OrderProto.OrderResponse.newBuilder();
            builder.setOrderId(request.getOrderId());
            builder.setRespCode("200");
            builder.setDesc("Order Submit Successfully");
            ctx.writeAndFlush(builder);
        }

        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            ctx.flush();
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            ctx.close();//发生异常时候，执行重写后的 exceptionCaught 进行资源关闭
        }
	}
	
    public static void main(String[] args) {
        OrderServer.bind(4040);
    }
	
	
	
	
}
